/*
 *  AdvancedKeyboardEventsMonitor.cs
 *  
 *  Monitors the keyboard events of a form or control.
 *  
 *  Copyright (c) 2009, Richard Bao Labs.
 *  
 *  The contents of this file are subject to the Mozilla Public License
 *  Version 1.1 (the "License"); you may not use this file except in
 *  compliance with the License. You may obtain a copy of the License at
 *  
 *  http://www.mozilla.org/MPL/
 *  
 *  Software distributed under the License is distributed on an "AS IS"
 *  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 *  the License for the specific language governing rights and limitations
 *  under the License.
 *  
 *  The Original Code is OpenPainter.
 *  
 *  The Initial Developer of the Original Code is OpenPainter.org.
 *  All Rights Reserved.
 */

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Collections.ObjectModel;

namespace RichControlLibrary
{
    /// <summary>
    /// Monitors the keyboard events of a form or control.
    /// </summary>
    public class AdvancedKeyboardEventsMonitor : IDisposable
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="AdvancedKeyboardEventsMonitor"/> by the specific
        /// <see cref="Control"/>.
        /// </summary>
        /// <param name="control"></param>
        public AdvancedKeyboardEventsMonitor(Control control)
        {
            Register(control);
        }

        /// <summary>
        /// Occurs when a key is pressed.
        /// </summary>
        public event KeyEventHandler KeyDown = null;

        /// <summary>
        /// Raises the <see cref="KeyDown"/> event.
        /// </summary>
        /// <param name="e">A <see cref="KeyEventArgs"/> that contains the event data.</param>
        protected virtual void OnKeyDown(KeyEventArgs e)
        {
            KeyEventHandler handler = KeyDown;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        /// <summary>
        /// Occurs when a key is released.
        /// </summary>
        public event KeyEventHandler KeyUp = null;

        /// <summary>
        /// Raises the <see cref="KeyUp"/> event.
        /// </summary>
        /// <param name="e">A <see cref="KeyEventArgs"/> that contains the event data.</param>
        protected virtual void OnKeyUp(KeyEventArgs e)
        {
            KeyEventHandler handler = KeyUp;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        private List<Keys> _pressedKeys = new List<Keys>();
        /// <summary>
        /// Gets the pressed keys.
        /// </summary>
        public ReadOnlyCollection<Keys> PressedKeys
        {
            get { return _pressedKeys.AsReadOnly(); }
        }

        /// <summary>
        /// Checkes whether the specified keys are pressed.
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public bool IsPressed(Keys key)
        {
            return _pressedKeys.Contains(key);
        }

        /// <summary>
        /// Gets a boolean value which indicates whether the Shift key is pressed.
        /// </summary>
        public bool IsShiftPressed
        {
            get { return (Control.ModifierKeys & Keys.Shift) == Keys.Shift; }
        }

        /// <summary>
        /// Gets a boolean value which indicates whether the Ctrl key is pressed.
        /// </summary>
        public bool IsCtrlPressed
        {
            get { return (Control.ModifierKeys & Keys.Control) == Keys.Control; }
        }

        /// <summary>
        /// Gets a boolean value which indicates whether the Alt key is pressed.
        /// </summary>
        public bool IsAltPressed
        {
            get { return (Control.ModifierKeys & Keys.Alt) == Keys.Alt; }
        }

        /// <summary>
        /// Gets the pressed modifier keys.
        /// </summary>
        public Keys PressedModifierKeys
        {
            get { return Control.ModifierKeys; }
        }

        /// <summary>
        /// Pauses the keyboard monitoring.
        /// </summary>
        public void Pause()
        {
            _control.KeyDown -= control_KeyDown;
            _control.KeyUp -= control_KeyUp;

            _pressedKeys.Clear();
        }

        /// <summary>
        /// Resumes the keyboard monitoring.
        /// </summary>
        public void Resume()
        {
            _control.KeyDown += control_KeyDown;
            _control.KeyUp += control_KeyUp;

            Form form = _control as Form;
            if (form != null)
            {
                form.KeyPreview = true;
            }

            _pressedKeys.Clear();
        }

        #region Private Members

        private Control _control = null;

        private void Register(Control control)
        {
            _control = control;
            Resume();
        }

        private void Unregister()
        {
            Pause();
            _control = null;
        }

        private void control_KeyDown(object sender, KeyEventArgs e)
        {
            if (_pressedKeys.Contains(e.KeyCode) == false)
            {
                _pressedKeys.Add(e.KeyCode);
                OnKeyDown(e);
            }
        }

        private void control_KeyUp(object sender, KeyEventArgs e)
        {
            if (_pressedKeys.Contains(e.KeyCode) == true)
            {
                _pressedKeys.Remove(e.KeyCode);
                OnKeyUp(e);
            }
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// Releases all resources used by <see cref="AdvancedKeyboardEventsMonitor"/> class.
        /// </summary>
        public void Dispose()
        {
            Unregister();
        }

        #endregion
    }
}
